#include "kernel.h"
#include  "global.h"
#include <stdio.h>
#include <unistd.h>

RTX:: RTX(){
	for( int i = 0; i < Num_Proc; i++ )
        {PCB_list[i] = NULL;}
        cur_proc=NULL;
        
        free_env_Q = new MsgEnvQ(Num_MsgEnv);
        blk_on_receive_Q=new PCBQ();
        timeout_Q=new MsgEnvQ();
        for( int i = 0; i < Num_PriorityLevels; i++ ){
            ready_Q[i] = new PCBQ();
            blk_on_resource_Q[i]=new PCBQ();
        }
        system_time=0;
        send_trace_buffer = new TraceBuffer[TraceBufferSize];
        receive_trace_buffer= new TraceBuffer[TraceBufferSize];
}

void RTX:: initialization(initTable init_table[]){
     /*init_MsgEn*/
    free_env_Q = new MsgEnvQ(Num_MsgEnv);
    /*initialize PCBs*/
    for(int i=0;i<Num_Proc;i++){
        //put the values in init_table into according fields in PCBs
        printf("PCB %d\n",i+1);
        PCB_list[i]=new PCB;
        PCB_list[i]->next=NULL;
        PCB_list[i]->msgQ=new MsgEnvQ();
        PCB_list[i]->pid=init_table[i].pid;
        PCB_list[i]->priority=init_table[i].priority;
        PCB_list[i]->status=init_table[i].status;
        PCB_list[i]->address=init_table[i].start_address;
        //put into ready queue if it is not an i-proc
        
        if(PCB_list[i]!=I_Process){
            rpq_enqueue(PCB_list[i]);
            printf("PCB [%d] is in ready queue\n",PCB_list[i]->pid);
        }   
        getcontext(&(PCB_list[i]->proContext));
        PCB_list[i]->proContext.uc_stack.ss_sp = PCB_list[i]->stack;
        PCB_list[i]->proContext.uc_stack.ss_flags = 0;
        makecontext(&PCB_list[i]->proContext, PCB_list[i]->address, 0);
        printf("context of %d is set\n",PCB_list[i]->pid);
  }

    /*initialize processes*/
    ucontext_t mainContext;
    getcontext(&mainContext);
    
     /*initialize trace buffer*/
      /*already did in RTX()*/
    
    /*switch to current process*/
     cur_proc=rpq_dequeue();
    cur_proc->status=Executing;
    printf("about to switch to first process from main\n");
    printf("The first process should be 0, and it is [%d]\n",cur_proc->pid);
    getchar();
    swapcontext(&mainContext, &(cur_proc->proContext));
}		 

void RTX::rpq_enqueue(PCB* pcb){
    ready_Q[pcb->priority]->enqueue(pcb);
    return;
}

PCB * RTX::rpq_dequeue(){
    int pri=0;
    PCB * temp=NULL;
    while(pri<4){
        if(ready_Q[pri]->getsize()!=0){//not empty
            temp=ready_Q[pri]->dequeue();
            return temp;
        }
        else//empty
            {pri++;}    
    }
    return NULL;
}

PCB * RTX::rpq_remqueue(int pid){
    PCB * temp1=get_PCB(pid);
    PCB * temp2=ready_Q[temp1->priority]->remqueue(pid);
    return temp2;
}

void RTX::blkq_enqueue(int blocked_type,PCB * pcb){
    switch(blocked_type){
        case Blocked_On_Receive:
            blk_on_receive_Q->enqueue(pcb);
            break;
        case Blocked_On_Resource:
            blk_on_resource_Q[pcb->priority]->enqueue(pcb);
            break;
        default:
            //cout<<"Error to enqueue to blocked queue!"
            return;
    }
    //cout<<"enqueued to blocked queue!"
    return;
}

PCB * RTX::blkq_dequeue(int blocked_type){
    PCB * temp =NULL;
    switch(blocked_type){
        case Blocked_On_Receive:
            temp=blk_on_receive_Q->dequeue();
            break;
        case Blocked_On_Resource:
            int pri=0;
            while(pri<4){
                if(blk_on_resource_Q[pri]->getsize()!=0){//not empty
                        temp=blk_on_resource_Q[pri]->dequeue();
                        break;
                 }
                else//empty
                {pri++;} 
            }
            break;
    }
    return temp;
}

PCB * RTX:: get_PCB(int target_pid){
    PCB * temp_PCB;
    int i=0;
    //printf("\n the PCB we want to find is %i\n",target_pid);
    while(i<Num_Proc){
        temp_PCB=PCB_list[i];
        if (temp_PCB->pid==target_pid)
        {
           //printf("\n the search PCB got the PCB %i\n",temp_PCB->pid);
           return temp_PCB;                            
        }
        else
        {i++;
        //printf("\n the search PCB go to the next PCB \n");
        }
    }
    //printf("\n the search PCB DID NOT got the PCB \n");
    return NULL;
}			 

void RTX::process_switch(){
    PCB * prev_proc=cur_proc;        
    cur_proc=rpq_dequeue();
    cur_proc->status=Executing;
    context_switch(prev_proc);
    return;
}

void RTX::context_switch(PCB * next_proc){
    swapcontext(&(prev_proc->proContext),&(cur_proc->proContext));
    return;
}	

//the primitives!!	
int RTX::send_message(int dest_pid, MsgEnv *msg_envelope )
{//made some changes here, we only need one argument for this function since the envelope passed into the send message function will contain the target pid.
 //We need a way to get the PCB pointer from the pid. It should be easy because we know which process the message is going to be sent to
      PCB *dest_PCB;
      
      msg_envelope->target_pid = dest_pid;
      printf("\n the dest process for send is %i \n",dest_pid);
      dest_PCB=search_PCB(dest_pid);//dest_PCB is a pointer to the destination process
      printf("\n search pcb complted\n");
      printf("\n pid of the dest process is %i \n",dest_PCB->pid);
      dest_PCB->msgQ->enqueue(msg_envelope);//put msg onto msg Q of dest_PCB
      
      printf("\n msg inqueued\n");
 	//if (dest_process_id.status == 3)//however, if destination process status=blocked on receive
 	//{
    //            dest_PCB.status=2//set status to ready
 	// 	rpq.enqueue (Msg_Env, dest_PCB);
    //           addTrace(Msg_Env);
    //    }
//printf("\nmsg received!\n");
return 1;
}


MsgEnv * RTX:: receive_message( )
{
 	//MsgEnv *msg_env;
 	MsgEnv *temp;
 	//printf("the size fo the msgQ on Process P is %s\n",PCB_list[ProcessP]->msgQ->getsize());
        while (cur_proc->msgQ->getsize()==0 ) {//if no msg to receive or status = 5(NO BLOCK RECEIVE)
                //printf("msgQ on Process is empty\n");
                return NULL;
        //addtrace(MsgEnv*);//not in partial
        }
        temp=cur_proc->msgQ->dequeue(); 
        printf("dequeue in recieve success\n");
        return temp;
}

int RTX:: send_console_chars(MsgEnv * message_envelope ){
//Used to display text on CRT

//Send MsgEnv pointer and process ID to CRT Iprocess
message_envelope->sender_pid=cur_proc->pid;
message_envelope->target_pid=1;//CRT==1
message_envelope->type=4;//request_output

send_message(message_envelope->sender_pid,message_envelope);

//Force CRT i_proc to run once
//CRT_iproc();

}

int RTX:: get_console_chars(MsgEnv * message_envelope ){
//Used to recieve input from user/KB
if(message_envelope==NULL)
{return -1;}
message_envelope->sender_pid=cur_proc->pid;
message_envelope->target_pid=0; //KB Iproc
message_envelope->type=request_input;//request_input
//kbd_handler(SIGUSR1);
printf("\n msg chars got\n");
return 0;
//force Run KB i_process once
//KB_iproc(cur_proc->pid);

}

